﻿using System;
using System.Collections.Generic;
using System.Timers;
using System.Windows.Controls;
using BitsOfStuff.MyActions;
using BitsOfStuff.Support;

namespace BitsOfStuff
{
    /// <summary>
    /// Serializable class that holds all user defined ways of writng chars
    /// </summary>
    [Serializable]
    public class UserProfile
    {
        private readonly string _userProfileName;

        private readonly MyUserProfileNodle _rootNodle;

        private readonly MyCharNotFoundAction _charNotFoundAction;

        private Dictionary<string, List<MyAction>> _footStepsDictionary;

        private Dictionary<char, List<string>> _sequenceDictionary;

        private List<MyAction> _primaryDecisionList;
        private List<MyAction> _secundaryDecisionList;

        /// <summary>
        /// Generich Action that inform us about unsucesful recognition , we dont need to creatne new action again and againt , but we will use this precreated action
        /// </summary>
        /// <returns></returns>
        public MyCharNotFoundAction getCharNotFoundAction()
        {
            return _charNotFoundAction;
        }

        [NonSerialized]
        private MyCharRecognizer _charRecognizer;

        private void updateCharRecognizer(MyCharRecognizer myCharRecognizer)
        {
            _charRecognizer = myCharRecognizer;
        }

        private List<MyAction> allActions;

        /// <summary>
        /// Constructor of user profile
        /// </summary>
        /// <param name="userProfileName">name of user profile</param>
        /// <param name="myCharRecognizer">recognizer that manage this profile</param>
        public UserProfile(string userProfileName, MyCharRecognizer myCharRecognizer)
        {
            _userProfileName = userProfileName;
            _rootNodle = new MyUserProfileNodle(this);
            allActions = new List<MyAction>();
            _charRecognizer = myCharRecognizer;
            _footStepsDictionary = new Dictionary<string, List<MyAction>>();
            _charNotFoundAction = new MyCharNotFoundAction();
            _sequenceDictionary = new Dictionary<char, List<string>>();
            _primaryDecisionList = new List<MyAction>();
            _secundaryDecisionList = new List<MyAction>();
        }

        /// <summary>
        /// Funstion that provide name of user profile
        /// </summary>
        /// <returns>user profile name</returns>
        public string getUserProfileName()
        {
            return _userProfileName;
        }

        /// <summary>
        /// This function return rootNodle of vector tree
        /// </summary>
        /// <returns></returns>
        public MyUserProfileNodle getRootNodle()
        {
            return _rootNodle;
        }

        /// <summary>
        /// Function that store new char!
        /// </summary>
        /// <param name="sequence">Vector sequence of new char</param>
        /// <param name="action">action , that containt new char</param>
        public void addSequenc(List<int> sequence, MyAction action)
        {
            List<int> sequenceCopy = new List<int>(sequence);
            addSequenceFootStep((MyAction)action.Clone());
            _rootNodle.addSequence(sequence, action);
            allActions.Add(action);
            if (action is MyShowAction)
            {
                var showAction = action as MyShowAction;
                if (_sequenceDictionary.ContainsKey(showAction.getChar()))
                {
                    var newList = _sequenceDictionary[showAction.getChar()];
                    _sequenceDictionary.Remove(showAction.getChar());
                    newList.Add(SupportFunctions.convertFromSequenceToString(sequenceCopy));
                    _sequenceDictionary.Add(showAction.getChar(), newList);
                }
                else
                {
                    _sequenceDictionary.Add(showAction.getChar(), new List<string>() { SupportFunctions.convertFromSequenceToString(sequenceCopy) });
                }
            }
        }

        private void addSequenceFootStep(MyAction action)
        {
            //First footstep action will be record as autoritative
            var autoritativeAction = (MyAction)action.Clone();
            autoritativeAction.setFinal();

            var actualFootStepLast = FootstepRecognition.getFootStepStringRecord();
            addOneFooStep(autoritativeAction, actualFootStepLast);

            while (FootstepRecognition.haveNoFootStepToRecord())
            {
                var actualFootStep = FootstepRecognition.getFootStepStringRecord();
                addOneFooStep(action, actualFootStep);
            }
        }

        private void addOneFooStep(MyAction action, string key)
        {
            if (_footStepsDictionary.ContainsKey(key))
            {
                var footStepList = new List<MyAction>(_footStepsDictionary[key]);
                _footStepsDictionary.Remove(key);
                // removes duplicity of actions in footsteps
                if (!footStepList.Exists(curentAction => curentAction.Equals(action)))
                {
                    footStepList.Add(action);
                }
                _footStepsDictionary.Add(key, footStepList);
            }
            else
            {
                _footStepsDictionary.Add(key, new List<MyAction>() { action });
            }
        }

        /// <summary>
        /// Function for alocating local resources on profile after deserialization
        /// </summary>
        /// <param name="inkCanvas">local inkCanvas used for drawing </param>
        public void assignInkCanvas(InkCanvas inkCanvas)
        {
            foreach (MyAction allAction in allActions)
            {
                allAction.assignInkCanvas(inkCanvas);
            }
        }

        /// <summary>
        /// Function that provide us finding vector action based on vector sequenc and start in given Noodle of tree
        /// </summary>
        /// <param name="sequence">vector sequece to find</param>
        /// <param name="curentProfileNoodle">start noodle</param>
        /// <returns>All vector actions for sequence and noodle kontext</returns>
        public List<MyAction> findSequence(List<int> sequence, ref MyUserProfileNodle curentProfileNoodle)
        {
            var result = curentProfileNoodle.findSequence(sequence, ref curentProfileNoodle);
            return result;
        }

        /// <summary>
        /// Function that return timer used for decision actions
        /// </summary>
        /// <returns></returns>
        public Timer getTimer()
        {
            return _charRecognizer.getTimer();
        }

        /// <summary>
        /// Our private Event handler when myAction is found
        /// </summary>
        /// <param name="myAction"></param>
        public void Event(MyAction myAction)
        {
            _charRecognizer.Event(myAction);
        }

        /// <summary>
        /// After loaded this class for serialization, we need to set references for local resources
        /// </summary>
        /// <param name="myCharRecognizer">local myCHharRecognizer</param>
        public void setNewCharRecognizer(MyCharRecognizer myCharRecognizer)
        {
            _charRecognizer = myCharRecognizer;
        }

        /// <summary>
        /// Function for geting actions from Footsteps dictionary
        /// </summary>
        /// <param name="key">footstep</param>
        /// <returns>footsteps actions</returns>
        public List<MyAction> getFootStepAction(string key)
        {
            if (key == null)
                return null;
            if (_footStepsDictionary.ContainsKey(key))
                return _footStepsDictionary[key];
            else
                return null;
        }

        /// <summary>
        /// Function for finding all fotsteps for given char , used for speculating analysing
        /// </summary>
        /// <param name="key">char</param>
        /// <returns>all footsteps that belongs for char</returns>
        public List<string> getFootStepSequence(char key)
        {
            if (_sequenceDictionary.ContainsKey(key))
            {
                return _sequenceDictionary[key];
            }
            return new List<string>();
        }

        /// <summary>
        /// Function for adding Decision Action
        /// </summary>
        /// <param name="primary">primary action whit few strokes</param>
        /// <param name="secundary">secundary action whit more strokes</param>
        public void addDecisionAction(MyAction primary, MyAction secundary)
        {
            _primaryDecisionList.Add(primary);
            _secundaryDecisionList.Add(secundary);
        }

        /// <summary>
        /// Function that decide,if action need decision
        /// </summary>
        /// <param name="primary">Primary action ( char ) ... that has few strokes</param>
        /// <param name="secondary">Secondary action ... that has more strokes</param>
        /// <param name="decisionAction">out actions , marged primary and secondary action</param>
        /// <returns></returns>
        public bool tryIfitsOnDecisionState(MyAction primary, MyAction secondary, out MyDecisionAction decisionAction)
        {
            if (_primaryDecisionList.Contains(primary))
            {
                int index = _primaryDecisionList.IndexOf(primary);
                if (_secundaryDecisionList[index].CompareTo(secondary) == 0)
                {
                    decisionAction = new MyDecisionAction(primary);
                    return true;
                }
            }

            if (_secundaryDecisionList.Contains(primary))
            {
                int index = _primaryDecisionList.IndexOf(primary);
                if (_primaryDecisionList[index].CompareTo(secondary) == 0)
                {
                    decisionAction = new MyDecisionAction(primary);
                    return true;
                }
            }

            decisionAction = null;
            return false;
        }

        /// <summary>
        /// Function for removing char from all internat data holders
        /// </summary>
        /// <param name="removeAction">Action , that holds char that will be removed</param>
        public void removeStoredChar(MyAction removeAction)
        {
            if (removeAction is MyShowAction)
            {
                char c = ((MyShowAction)removeAction).getChar();
                removeVectorChar(c);
                removeFootstepChar(c);
            }
        }

        private void removeFootstepChar(char c)
        {
            var keysToremove = new List<string>();
            foreach (KeyValuePair<string, List<MyAction>> keyValuePair in _footStepsDictionary)
            {
                keyValuePair.Value.RemoveAll(a => (a is MyShowAction) && (((MyShowAction)a).getChar() == c));
                if (keyValuePair.Value.Count == 0)
                    keysToremove.Add(keyValuePair.Key);
            }
            foreach (string s in keysToremove)
            {
                _footStepsDictionary.Remove(s);
            }
        }

        private void removeVectorChar(char c)
        {
            _rootNodle.removeAction(c);
        }
    }
}